--[[
    Builder 预编译模块  -   slk
        >先读静态物编 再读动态物编
        >如 相同ID 覆盖静态物编 | 如无ID 生成ID
        >生成表结构 <地图>
        >io写出<map>\xLua\Cache\xslk\*.lua作为缓存常量供框架读取

    xslk.unit:new {
        --模板
        _parent = 'uaod',

        --留nil 系统将自动生成
        _id =   'a001',

        HP = 100,

    }

]]
local mt = {}
mt.__index = mt

local slk_type = { A = 'ability', B = 'buff', D = 'destructable', C = 'doodad', I = 'item', M = 'misc', T = 'txt', U = 'unit',  G = 'upgrade' }
local store = {}


xslk = {}



function mt:newId(isHero)
    local str
    self._id = self._id + 1
    str = tostring(self._id):convert(36):lower()

    str =  ('0'):string( 4 - str:len()  ) .. str --前端补0
    if str:sub(1,-4) == '0' then
        --替换首位 0 为 物编首字母
        str = self.prefix .. str:sub(2)
    end

    if self.type == 'unit' and isHero then --单位需要考虑开头大写转化为英雄
        str = str:sub(1,1):upper() .. str:sub(2)
    end
    
    if store[str] then --已被使用的id
        return self:newId(isHero)
    end
    self.count = self.count + 1
    store[str] = self.type
    return str
end

--将slk全部作为lni 保存
local function SaveLni()
    for key, self in pairs(xslk) do
        local _type = self.type
        local count_dynamic = self.count
        local count_static = self.count_static
        local count = count_dynamic + count_static
        if count > 0 then
            print(
                ('   xslk 正在处理 %3d 个 %12s 物编\t静态[ %3d  ]  动态[ %3d  ]'):format(count, key, count_static, count_dynamic)
            )
            self.lni:save()
        end
    end
end

--新建物编对象 返回id
function mt:new(slk)
    local _type = self.type --'ability', 'buff', 'destructable', 'doodad', 'item', 'misc', 'txt', 'unit', 'upgrade'
    local id = slk.id --在misc中可以用来指代节点名称
    local lni = xslk[_type].lni._ini
    assert( slk._parent , ('xslk[ %s ]:new 物编时 %s 未声明模板_parent'):format(self.type, id or '[匿名id]') )

    local _parent = slk._parent
    local tmpParent = _parent
    --以自定义物编为模板
    while tmpParent and lni[tmpParent] do

        --遍历父模板 将父模板中（修改过 并且 当前slk里未声明） 的内容添加进当前slk
        for key, value in pairs(lni[tmpParent]) do
            if (not slk[key]) and key ~= 'id' then
                slk[key] = value
            end
        end
        --溯源
        _parent = lni[tmpParent]._parent
        if  _parent and _parent ~= tmpParent then
            tmpParent = _parent
            
        else
            --返回最后一个模板(原生模板)
            _parent = tmpParent
            tmpParent = nil
        end
    end
    slk._parent = _parent

    if not id then --无ID 系统生成id
        id = self:newId(slk.isHero)
        table.insert( self.dynamics, id )
    else
        --存相同id的静态物编。直接覆盖
        if slk.type == 'unit' then
            if id:byte() <= 90  then --A 65   Z 90
                slk.isHero = true
            end
        end
        --检测作者所指定id是否已被使用
        if store[id] then
            --如果类型相同则覆盖 类型不同则抛出错误
            assert(self.type == store[id] , ('xslk[ %s ]:new 物编时 %s 占用了 %s 的 id 不同类型物编不允许ID占用'):format(self.type, id, store[id]) )
        end
        store[ id ] = self.type
        self.count = self.count + 1
    end

    slk.id = id



    for key, value in pairs(slk) do
        if type(value) == 'function' or
            type(value) == 'userdata'
        then
            slk[key] = nil
        end
    end
    lni[id] = slk
    return id
end



--保存缓存lua
local function SaveCache(filename)
    local lua_ids  = {}  --存储id  用来遍历 xslk.unit.ids[index] 得出id 暂时想不到有什么具体用处。
    local lua_n2i = { }
    local lua_dynmic = {} --动态物编的ids
    local lua_static = {}

    local name
    local lua_withoutname = {}
    local lua_names = {} --重复名

    local sort = {
        'item', 'unit', 'ability', 'buff', 'destructable', 'doodad', 'upgrade'
    }
    for _, key in ipairs(sort) do
        -- slk == xslk[unit/item....]
        local slk = xslk[ key ]

        table_insert( lua_ids, ('xslk.%s._ids = {\n'):format(key) )
        table_insert( lua_n2i, ('xslk.%s._n2i = {\n'):format(key) )
        table_insert( lua_dynmic, ('xslk.%s._dynmic = {\n'):format(key) )

        --顺序写入动态id
        for i,id in ipairs( slk.dynamics ) do
            table_insert( lua_dynmic, ('"%s",\n'):format( id ) )
        end
        
        ---n2i所需要的缓存
        for id, lni in slk.lni:forEach() do
            name = lni.Name
            if name then
                --记录id
                table_insert( lua_ids, ('"%s",\n'):format(id) )
                --name重名
                if lua_names[name] then
                    table_insert(lua_names[name],  id)
                else
                    table_insert( lua_n2i, ('["%s"] = "%s",\n'):format( name, id ) )
                    table_insert( lua_names, name )
                    lua_names[name] = { }
                    table_insert(lua_names[name],  id)
                end

            else
                --记录下没名字的
                table_insert(lua_withoutname, { id = id , _parent = lni._parent})
            end

        end

        table_insert(lua_ids, '}\n' )
        table_insert(lua_n2i, '}\n' )
        table_insert(lua_dynmic, '}\n' )

        --没有指定名字的物编从jass.slk模板中读
        for _, val in ipairs(lua_withoutname) do
            local id = val.id
            local _parent = val._parent
            table_insert( lua_n2i, ('xslk.%s._n2i[ jslk.%s.%s.Name ] = "%s"\n'):format( slk.type, slk.type, _parent, id ) )
            table_insert( lua_ids, ('table.insert(xslk.%s._ids,"%s")\n'):format(slk.type, id) )
        end
        lua_withoutname = {}
        
        --一个名字对应多个id(重名物编)
        for _, name in ipairs(lua_names) do

            local count = lua_names[name][0]
            if count > 1 then
                table_insert( lua_n2i, ('xslk.%s._n2i[ "%s" ] = {\n'):format( slk.type, name ) )
                for index, id in ipairs(lua_names[name]) do
                    table_insert( lua_n2i, ('"%s",\n'):format( id ) )
                end
                table_insert( lua_n2i, ('}\n') )

            end
        end
        lua_names = {}

        --静态物编 让框架为其生成一个_parent字段
        for index, list in ipairs(slk.statics) do
            local id = list.id
            local obj = list.slk
            table_insert( lua_static, ('xslk.%s:static("%s","%s")\n'):format( slk.type, id ,obj._parent ) )
        end

    end

    local lua =
([[--雪月框架 - 自动生成 xslk 缓存
--ids
%s
----------

--n2i
%s
----------

--dynmic
%s
----------

--static
%s
----------
]]):format(
    table.concat(lua_ids),
    table.concat(lua_n2i),
    table.concat(lua_dynmic),
    table.concat(lua_static)
 )

    write(filename, lua)
end --saveCache




function builder:xslkBuild ()
    require 'compile.xslk.pretreatment'
    --初始化静态物编
    for key,v in pairs(slk_type) do
        local t = {
            type = v,
            lni = ini:open( (Root .. "\\debug\\" ..  builder.map  .. "\\temp\\table\\%s.ini"):format(v) ), --载入对应lni[静态物编]
            count = 0, --用来作为动态物编数量
            _id = 0,    --用来计算id
            prefix = key, --前缀
        }
        t.count_static = table.len(t.lni._ini)
        setmetatable(t, mt)

        xslk[v] = t
        xslk[v].statics = {}
        xslk[v].dynamics = {}
        for id, slk in pairs(t.lni._ini) do
            store[id] = v
            table_insert( xslk[v].statics, { id = id, slk = slk } )
        end
    end
    self:itemPreInit()

    local _p = package.path
    package.path = Root .. '\\debug\\' .. self.map .. '\\temp\\map\\?.lua'
    package.path = package.path ..';'.. Root .. '\\debug\\' .. self.map .. '\\temp\\map\\?\\init.lua'

    --xslk预处理lua 只支持读取读取路径下的其他预处理lua
    local p1 = Root .. '\\debug\\' .. self.map .. '\\temp\\map\\xslk\\init.lua'
    mkdir(p1:sub2('\\'))

    local file = io.open(p1, 'a+')--创建空文件 
    if file then file:close()  end

    print('[3] xslk 生成物编...')
    require ( 'xslk.init' )  --从项目根目录的xlsk文件夹下载入init

    -- XG_Framework\debug\<map>\temp\scripts\xslk\init.lua
    --恢复path
    package.path = _p

    local items = xslk.item.lni._ini
    for id,item in pairs( items ) do
        if not( item.desc or item.ATTR ) then
            goto next_item
        end

        xslk.item:processing( item )

        ::next_item::
    end

    SaveCache( p1:sub2('\\') .. 'Cache.lua' )
    
    SaveLni()
    
    --assert(false) --用来打断过程 调试查看生成的文件
end
